﻿#include "../../../includes/multimedia/rtsp/crtspclient.h"
#include "../../../includes/multimedia/rtsp/crtspserver.h"
#include "../../../includes/multimedia/rtsp/h264source.h"
#include "../../../includes/QsLog/QsLog.h"
#include "../../../includes/common/common.h"

#include <ctime>
#include <random>
#include <QTime>

QStringList MethodToString =
{
    "OPTIONS",
    "DESCRIBE",
    "ANNOUNCE",
    "RECORD",
    "SETUP",
    "PLAY",
    "TEARDOWN",
    "GET_PARAMETER",
    "RTCP",
    "NONE"
};

CRtspClient::CRtspClient(QObject *parent,qintptr socketDescriptor)
    : QTcpSocket(parent),
      m_clientID(socketDescriptor),
      m_method(MNONE),
      m_cseq(0),
      m_rtp_port(0),
      m_rtcp_port(0),
      m_port(0),
      has_key_frame_(false),
      is_play(false),
      m_is_readyFinished(false),
      m_ConnectionMode(RTSP_CLIENT),
      m_rtspServer(NULL),
      m_isreciverPacketHeader(false),
      m_reciverPacketSize(0)
{
    this->setSocketDescriptor(m_clientID);
    m_H264Source.setRtspClient(this);

    std::random_device rd;
    memset(&m_RtpHeader,0,sizeof(m_RtpHeader));
    m_RtpHeader.version = RTP_VERSION;
    m_RtpHeader.seq = 0;
    m_RtpHeader.ts = htonl(rd());
    m_RtpHeader.ssrc = htonl(rd());
    m_packet_seq = rd()%0xffff;

    QObject::connect(&m_HeartTimeOutTimer,
                     &QTimer::timeout,
                     this,
                     &CRtspClient::handleHeartTimeOut);
    m_HeartTimeOutTimer.setSingleShot(true);

    connect(this, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
    connect(this, SIGNAL(connected()), this, SLOT(onConnected()));
    connect(this, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
}

CRtspClient::~CRtspClient()
{
    m_rtpUdpSocket.close();
    m_rtcpUdpSocket.close();
}

/**
 * @brief CRtspClient::onConnected 连接成功后的处理
 */
void CRtspClient::onConnected()
{

}

/**
 * @brief CRtspClient::onDisconnected 连接断开后的处理
 */
void CRtspClient::onDisconnected()
{
    m_HeartTimeOutTimer.stop();

    emit sockDisConnect(m_clientID);
}

/**
 * @brief CRtspClient::HandleFrame 处理视频帧
 * @param frame
 * @return
 */
bool CRtspClient::HandleFrame(AVFrame *frame)
{
    return m_H264Source.HandleFrame(frame);
}

/**
 * @brief CRtspClient::onReadyRead 处理接收数据
 */
void CRtspClient::onReadyRead()
{
    QByteArray reciverData = this->readAll();

    if(reciverData.isEmpty())
        return;

    if(m_is_readyFinished && m_rtspServer)
    {
        m_reciverByteArray.append(reciverData);

        if(!m_isreciverPacketHeader &&
           m_reciverByteArray.size() >= RTP_TCP_HEAD_SIZE)
        {
            memcpy(&m_reciverPacket,m_reciverByteArray.constData(),RTP_TCP_HEAD_SIZE);

            if(m_reciverPacket.type == '$')
            {
                m_reciverPacketSize = ((m_reciverPacket.size1<<8)|m_reciverPacket.size2);
                //m_reciverByteArray.remove(0,RTP_TCP_HEAD_SIZE);
                m_isreciverPacketHeader=true;
            }
        }

        if(m_isreciverPacketHeader &&
           m_reciverByteArray.size() >= m_reciverPacketSize)
        {
            m_rtspServer->HandleFrameGroup(m_rtp_suffix,&m_reciverByteArray.mid(RTP_TCP_HEAD_SIZE,m_reciverPacketSize));

            m_reciverByteArray.remove(0,m_reciverPacketSize+RTP_TCP_HEAD_SIZE);
            m_reciverPacketSize=0;
            m_isreciverPacketHeader=false;
        }
    }
    else
    {
        if(!HandleRtspRequest(reciverData))
            return;
    }

    //emit signalsNetBinary(m_clientID,reciverData);
}

/**
 * @brief CRtspClient::send2
 * @param data
 * @param size
 * @return
 */
qint64 CRtspClient::send2(const char *data,qint32 size)
{
    return send(QByteArray(data,size));
}

/**
 * @brief CRtspClient::send 封包并发送
 * @param data 要发送的数据
 *
 * @return 返回发送成功的数据
 */
qint64 CRtspClient::send(QByteArray data)
{
    if(this->state() !=  QTcpSocket::ConnectedState ||
       data.isEmpty())
    {
        return -1;
    }

    QByteArray sendData = data;
    qint64 m_sendsize,m_totalsize;
    int m_sendcount = 0;

    m_totalsize = sendData.size();
    m_sendsize=0;

    do
    {
        QByteArray psendbytearray;

        if(sendData.size()-m_sendsize < BUF_SIZE)
            psendbytearray = sendData.mid(m_sendsize);
        else
            psendbytearray = sendData.mid(m_sendsize,BUF_SIZE);

        m_sendsize += this->write(psendbytearray);

        if(m_sendsize <= 0) m_sendcount+=1;

        // 如果5次数据都没发送成功，就跳出
        if(m_sendcount > 5)
            break;
    }
    while(m_sendsize < m_totalsize);

    return m_sendsize;
}

/**
 * @brief CRtspClient::HandleCmdOption
 */
void CRtspClient::HandleCmdOption(void)
{
    char buf[256];
    memset((void*)buf, 0, 256);
    snprintf((char*)buf, 256,
            "RTSP/1.0 200 OK\r\n"
            "CSeq: %u\r\n"
            "Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY\r\n"
            "\r\n",
            m_cseq);

    send2(buf,strlen(buf));
}

/**
 * @brief CRtspClient::BuildServerErrorRes
 * @param buf
 * @param buf_size
 * @return
 */
int CRtspClient::BuildServerErrorRes(const char* buf, int buf_size)
{
    memset((void*)buf, 0, buf_size);
    snprintf((char*)buf, buf_size,
            "RTSP/1.0 500 Internal Server Error\r\n"
            "CSeq: %u\r\n"
            "\r\n",
            m_cseq);

    return (int)strlen(buf);
}

int CRtspClient::BuildDescribeRes(const char* buf, int buf_size, const char* sdp)
{
    memset((void*)buf, 0, buf_size);
    snprintf((char*)buf, buf_size,
            "RTSP/1.0 200 OK\r\n"
            "CSeq: %u\r\n"
            "Content-Length: %d\r\n"
            "Content-Type: application/sdp\r\n"
            "\r\n"
            "%s",
            m_cseq,
            (int)strlen(sdp),
            sdp);

    return (int)strlen(buf);
}

const char* CRtspClient::GetSdpMessage(void)
{
    char buf[2048] = {0};

    snprintf(buf, sizeof(buf),
            "v=0\r\n"
            "o=- 9%ld 1 IN IP4 %s\r\n"
            "t=0 0\r\n"
            "a=control:*\r\n" ,
            (long)std::time(NULL), convert_to_ipv4_addr(this->peerAddress()).toStdString().c_str());

    snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
            "m=video %hu RTP/AVP 96\r\n",
            0);

    snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
            "%s\r\n",
            m_H264Source.GetAttribute());

    snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
            "a=control:track%d\r\n", 0);

    return buf;
}

void CRtspClient::HandleCmdRecord(void)
{
    uint32_t sessionid = GetRtpSessionId();

    char buf[256];
    memset((void*)buf, 0, 256);
    snprintf((char*)buf, 256,
            "RTSP/1.0 200 OK\r\n"
            "CSeq: %u\r\n"
            "Session: %u\r\n",
            "\r\n"
            " %s\r\n"
            "Session: %u\r\n",
            "nTransport: RTP/AVP/TCP;unicast;mode=record;interleaved=%hu-%hu\r\n"
            "\r\n",
            m_cseq,
            sessionid,
            QTime::currentTime().toString("hh:mm:ss CST").toStdString().c_str(),
            sessionid,
            m_rtp_port,
            m_rtcp_port);

    snprintf((char*)buf + strlen(buf), 256 - strlen(buf), "\r\n");

    send2(buf,(int)strlen(buf));
}

void CRtspClient::HandleCmdAnnounce()
{
    char buf[256];
    memset((void*)buf, 0, 256);
    snprintf((char*)buf, 256,
            "RTSP/1.0 200 OK\r\n"
            "CSeq: %d\r\n"
            "ngsee0.9.6\r\n"
            "CSeq: %d\r\n"
            "Public: DESCRIBE, ANNOUNCE, SETUP, PLAY, PAUSE, RECORD, TEARDOWN\r\n",
            m_cseq,
            m_cseq-1);

    snprintf((char*)buf + strlen(buf), 256 - strlen(buf), "\r\n");

    send2(buf,(int)strlen(buf));
}

void CRtspClient::HandleCmdDescribe()
{
    m_RtpHeader.payload = m_H264Source.GetPayloadType();

    char buf[4096];
    int size = 0;
    const char* sdpStr = GetSdpMessage();
    if(sdpStr == "")
        size = BuildServerErrorRes(buf,4096);
    else
        size = BuildDescribeRes(buf, 4096, sdpStr);

    send2(buf,size);
}

void CRtspClient::HandleCmdPlay()
{
    char buf[256];
    memset((void*)buf, 0, 256);
    snprintf((char*)buf, 256,
            "RTSP/1.0 200 OK\r\n"
            "CSeq: %d\r\n"
            "Range: npt=0.000-\r\n"
            "Session: %u; timeout=60\r\n",
            m_cseq,
            (uint16_t)this->GetRtpSessionId());

    snprintf((char*)buf + strlen(buf), 256 - strlen(buf), "\r\n");

    send2(buf,(int)strlen(buf));

    is_play = true;
}

void CRtspClient::HandleCmdTeardown()
{
    char buf[256];
    memset((void*)buf, 0, 256);
    snprintf((char*)buf, 256,
            "RTSP/1.0 200 OK\r\n"
            "CSeq: %d\r\n"
            "Session: %u\r\n"
            "\r\n",
            m_cseq,
            (uint16_t)this->GetRtpSessionId());

    send2(buf,(int)strlen(buf));

    this->close();
}

uint32_t CRtspClient::GetRtpSessionId()
{
    return (uint32_t)((size_t)(this));
}

/**
 * @brief CRtspClient::reciverPendingRtspDatagram 处理rtsp端口数据接收
 */
void CRtspClient::reciverPendingRtspDatagram()
{
    if(m_rtcpUdpSocket.hasPendingDatagrams())
    {
        QByteArray preciverDatagramData;

        preciverDatagramData.resize(m_rtcpUdpSocket.pendingDatagramSize());

        if(m_rtcpUdpSocket.readDatagram(preciverDatagramData.data(),preciverDatagramData.size()) > 0)
        {
            m_HeartTimeOutTimer.setInterval(10000);
        }
    }
}

/**
 * @brief CRtspClient::handleHeartTimeOut 心跳处理
 */
void CRtspClient::handleHeartTimeOut()
{
    this->close();
}

void CRtspClient::HandleCmdSetup()
{
    std::random_device rd;
    m_serRtpPort = rd() & 0xfffe;
    m_serRtcpPort = m_serRtpPort + 1;

    m_rtpUdpSocket.setSocketOption(QAbstractSocket::SendBufferSizeSocketOption,50*1024);
    m_rtpUdpSocket.bind(QHostAddress::LocalHost, m_serRtpPort);

    m_rtcpUdpSocket.bind(QHostAddress::LocalHost,m_serRtcpPort);

    connect(&m_rtcpUdpSocket,
            &QUdpSocket::readyRead,
            this,&CRtspClient::reciverPendingRtspDatagram);

    char buf[256];
    memset((void*)buf, 0, 256);
    snprintf((char*)buf, 256,
            "RTSP/1.0 200 OK\r\n"
            "CSeq: %u\r\n"
            "Transport: RTP/AVP;unicast;client_port=%hu-%hu;server_port=%hu-%hu\r\n"
            "Session: %u\r\n"
            "\r\n",
            m_cseq,
            m_rtp_port,
            m_rtcp_port,
            m_serRtpPort,
            m_serRtcpPort,
            (uint16_t)this->GetRtpSessionId());

    send2(buf,(int)strlen(buf));
}

/**
 * @brief CRtspClient::sendUdpData2 发送UDP数据2
 * @param data
 * @return
 */
qint64 CRtspClient::sendUdpData2(QByteArray* data)
{
    if(data == NULL) return 0;

    qint64 tmpCurrentPos = 0;

    do
    {
        qint64 returnsize = m_rtpUdpSocket.writeDatagram(data->constData()+tmpCurrentPos,
                                                  data->size()-tmpCurrentPos,
                                                  QHostAddress(m_rtp_ip),
                                                  m_rtp_port);

        if(returnsize < 0)
        {
            this->close();
        }

        tmpCurrentPos+=returnsize;

        //qDebug()<<tmpCurrentPos<<" "<<returnsize<<" "<<data->size();
    }
    while(tmpCurrentPos < data->size());

    return tmpCurrentPos;
}

/**
 * @brief CRtspClient::sendUdpData 发送UDP数据
 * @param data
 * @param size
 * @return
 */
qint64 CRtspClient::sendUdpData(RtpPacket *pkt)
{
    if(pkt == NULL || m_rtp_port <= 0)
        return 0;

    this->SetFrameType(pkt->type);
    this->SetRtpHeader(pkt);

    qint64 returnsize = 0;
    if(is_play && has_key_frame_)
    {
        returnsize = m_rtpUdpSocket.writeDatagram((const char*)pkt->data.get()+4,
                                                  pkt->size-4,
                                                  QHostAddress(m_rtp_ip),
                                                  m_rtp_port);

        if(returnsize < 0)
        {
            this->close();
        }
    }

    return returnsize;
}

void CRtspClient::SetFrameType(uint8_t frame_type)
{
    if(!has_key_frame_ && (frame_type == 0 || frame_type == VIDEO_FRAME_I)) {
        has_key_frame_ = true;
    }
}

void CRtspClient::SetRtpHeader(RtpPacket *pkt)
{
    if(pkt == NULL) return;

    if(is_play && has_key_frame_)
    {
        m_RtpHeader.marker = pkt->last;
        m_RtpHeader.ts = htonl(pkt->timestamp);
        m_RtpHeader.seq = htons(m_packet_seq++);
        memcpy(pkt->data.get()+4, &m_RtpHeader, RTP_HEADER_SIZE);
    }
}


/**
 * @brief CRtspClient::HandleRtspRequest
 * @param data
 * @return
 */
bool CRtspClient::HandleRtspRequest(QByteArray data)
{
    m_HeartTimeOutTimer.setInterval(10000);

    QString tmpParams = QString(data);
    int pos = tmpParams.indexOf("\r\n");
    QString firstparam = tmpParams.mid(0,pos);
    QString secondparam = tmpParams.mid(pos+2);
    QStringList tmpparamlist = firstparam.split(" ");

    if(!tmpparamlist.empty())
    {
        for(int i=0;i<MethodToString.size();i++)
        {
            if(MethodToString[i] != tmpparamlist[0])
                continue;

            m_method = (Method)i;
            break;
        }
    }

    pos = secondparam.indexOf("CSeq");
    sscanf(secondparam.toStdString().c_str()+pos, "%*[^:]: %u", &m_cseq);

    switch(m_method)
    {
    case OPTIONS:
    {
        m_url = tmpparamlist[1];
        m_version = tmpparamlist[2];

        char ip[64] = {0};
        char suffix[64] = {0};

        if(sscanf(m_url.toStdString().c_str()+7, "%[^:]:%hu/%s", ip, &m_port, suffix) == 3) {

        }
        else if(sscanf(m_url.toStdString().c_str()+7, "%[^/]/%s", ip, suffix) == 2) {
            m_port = 554;
        }

        m_rtp_ip = ip;
        m_rtp_suffix = suffix;

        if(m_rtspServer) m_rtspServer->addClientToGroup(this);
    }
        break;
    case SETUP:
    {
        if(m_ConnectionMode == RTSP_CLIENT)
        {
            pos = secondparam.indexOf("RTP/AVP");
            if(pos > 0 && secondparam.indexOf("unicast") > 0)
            {
                sscanf(secondparam.toStdString().c_str()+pos, "%*[^;];%*[^;];%*[^=]=%hu-%hu",
                                        &m_rtp_port, &m_rtcp_port);
            }
        }
        else if(m_ConnectionMode == RTSP_PUSHER)
        {
            pos = secondparam.indexOf("RTP/AVP/TCP");
            if(pos > 0 && secondparam.indexOf("unicast") > 0)
            {
                sscanf(secondparam.toStdString().c_str()+pos, "%*[^;];%*[^;];%*[^;];%*[^=]=%hu-%hu",
                                        &m_rtp_port, &m_rtcp_port);
            }
        }
    }
        break;
    case ANNOUNCE:
    {
        m_ConnectionMode = RTSP_PUSHER;
        if(m_rtspServer) m_rtspServer->addClientToGroup(this);
    }
        break;
    case RECORD:
    {
        m_is_readyFinished=true;
    }
        break;
    default:
        break;
    }

    switch(m_method)
    {
    case OPTIONS:
        HandleCmdOption();
        break;
    case DESCRIBE:
        HandleCmdDescribe();
        break;
    case ANNOUNCE:
        HandleCmdAnnounce();
        break;
    case SETUP:
        HandleCmdSetup();
        break;
    case TEARDOWN:
        HandleCmdTeardown();
        break;
    case RECORD:
        HandleCmdRecord();
        break;
    case PLAY:
        HandleCmdPlay();
        break;
    default:
        break;
    }

    return true;
}
